/* -*- C++ -*- */

/****************************************************************************
** Copyright (c) quickfixengine.org  All rights reserved.
**
** This file is part of the QuickFIX FIX Engine
**
** This file may be distributed under the terms of the quickfixengine.org
** license as defined by quickfixengine.org and appearing in the file
** LICENSE included in the packaging of this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.quickfixengine.org/LICENSE for licensing information.
**
** Contact ask@quickfixengine.org if any conditions of this licensing are
** not clear to you.
**
****************************************************************************/

#ifndef FIX_DATADICTIONARY_H
#define FIX_DATADICTIONARY_H

#ifdef _MSC_VER
#pragma warning( disable : 4503 4355 4786 4290 )
#endif

#include "Fields.h"
#include "FieldMap.h"
#include "DOMDocument.h"
#include "Exceptions.h"
#include <set>
#include <map>
#include <string.h>
#include "FASTApi.h"

namespace FIX
{
	class FieldMap;
	class Message;
	class Session;

	/**
	* Represents a data dictionary for a version of %FIX.
	*
	* Generally loaded from an XML document.  The DataDictionary is also
	* responsible for validation beyond the basic structure of a message.
	*/

	class DataDictionary
	{

		friend class Message;
		friend class Session;
		typedef std::set < int > MsgFields;
		typedef std::map < std::string, MsgFields > MsgTypeToField;
		typedef std::set < std::string > MsgTypes;
		typedef std::set < int > Fields;
		typedef std::map < int, bool > NonBodyFields;
		typedef std::vector< int > OrderedFields;
		typedef int* OrderedFieldsArray;
		typedef std::map < int, TYPE::Type > FieldTypes;
		typedef std::set < std::string > Values;
		typedef std::map < int, Values > FieldToValue;
		typedef std::map < int, std::string > FieldToName;
		typedef std::map < std::string, int > NameToField;
		typedef std::map < std::pair < int, std::string > ,
			std::string  > ValueToName;
		typedef std::map < std::pair < std::string, int > ,
			std::pair < int, DataDictionary* > > FieldToGroup;

		typedef std::map < std::string, FIX::Message > MessageFactory;
		typedef std::map < std::string, int > Msg2TemplateId;
		typedef std::map < int, std::string > TemplateId2Msg;
		typedef std::vector<FastTag> FastTagVector;
		typedef std::map < u32, FastTagVector > TID2OrderedFields;
		typedef std::map < u32, u32 > CheckOrderedFields;


	public:
		DataDictionary();
		DataDictionary( const DataDictionary& copy );
		DataDictionary( std::istream& stream ) throw( ConfigError );
		DataDictionary( const std::string& url ) throw( ConfigError );
		virtual ~DataDictionary();

		void readFromURL( const std::string& url ) throw( ConfigError );
		void readFromDocument( DOMDocumentPtr pDoc ) throw( ConfigError );
		void readFromStream( std::istream& stream ) throw( ConfigError );

		int AddXMLFASTField(DOMDocument* pDoc, DOMNode* pMessageFieldNode, bool &bMsgTypeFound,
			int tid, std::string &msgType, bool &bRequired, int &fieldSlot, DataDictionary &DD,
			int &ifield, const std::string & msgName, bool &bpMapReq) throw( ConfigError );

		void addXMLFASTGroup( DOMDocument* pDoc, DOMNode* pNode,
			const std::string& msgtype, int tid, int &groupFieldSlot, 
			DataDictionary& DD, bool &bPmapReq) throw( ConfigError );
		void readFromFASTDocument( DOMDocumentPtr pDoc )throw( ConfigError );

		int* getOrderedFields() const;

		// storage functions
		void setVersion( const std::string& beginString )
		{
			m_beginString = beginString;
			m_hasVersion = true;
		}
		std::string getVersion() const
		{
			return m_beginString;
		}

		void addField( int field )
		{
			m_fields.insert( field );
			m_orderedFields.push_back( field );
		}

		void addFASTField( int field )
		{
			if ( !isField(field) )
			{
				m_fields.insert( field );
				m_orderedFields.push_back( field );
			}
		}


		void addFieldName( int field, const std::string& name )
		{
			if( m_names.insert( std::make_pair(name, field) ).second == false )
				throw ConfigError( "Field named " + name + " defined multiple times" );
			m_fieldNames[field] = name;
		}
		void addFASTFieldName( int field, const std::string& name )
		{
			if( m_names.insert( std::make_pair(name, field) ).second == false )
				return;
			m_fieldNames[field] = name;
		}
		bool getFieldName( int field, std::string& name ) const
		{
			FieldToName::const_iterator i = m_fieldNames.find( field );
			if(i == m_fieldNames.end()) return false;
			name = i->second;
			return true;
		}

		bool getFieldTag( std::string name, int& field ) const
		{
			NameToField::const_iterator i = m_names.find( name );
			if(i == m_names.end()) return false;
			field = i->second;
			return true;
		}

		void addValueName( int field, const std::string& value, const std::string& name )
		{
			m_valueNames[std::make_pair(field, value)] = name;
		}

		bool getValueName( int field, const std::string& value, std::string& name ) const
		{
			ValueToName::const_iterator i = m_valueNames.find( std::make_pair(field, value) );
			if(i == m_valueNames.end()) return false;
			name = i->second;
			return true;
		}

		bool isField( int field ) const
		{
			return m_fields.find( field ) != m_fields.end();
		}

		void addMsgType( const std::string& msgType )
		{
			m_messages.insert( msgType );
		}

		void addTemplateID( const std::string& msgType, long tid )
		{
			m_Message2TemplateId.insert( std::make_pair(msgType, tid) );
			m_TemplateId2Message.insert( std::make_pair(tid, msgType) );
		}

		bool isMsgType( const std::string& msgType ) const
		{
			return m_messages.find( msgType ) != m_messages.end();
		}

		bool isTagType( unsigned int tid ) const
		{
			return m_SortedFieldValues.find( tid ) != m_SortedFieldValues.end();
		}

		void addMsgField( const std::string& msgType, int field )
		{
			m_messageFields[ msgType ].insert( field );

		}

		bool addMsgFieldTag( FastTag ft )
		{

			if ( m_SortedFieldValues.find(ft.get_tid()) != m_SortedFieldValues.end())
			{
				bool isPresent = true;
				for ( FastTagVector::iterator start = m_SortedFieldValues[ft.get_tid()].begin(); start != m_SortedFieldValues[ft.get_tid()].end(); start++ )
				{
					if ( start->get_num() ==  ft.get_num() )
					{
						isPresent = false;
					}
				}
				if ( isPresent )
				{
					m_SortedFieldValues[ ft.get_tid() ].push_back( ft );
				}
			}
			else
			{
				m_SortedFieldValues[ ft.get_tid() ].push_back( ft );
			}
			if (ft.get_op() == eFAST_OP_NONE || 
				ft.get_op() == eFAST_OP_DELTA || 
				(ft.get_op() == eFAST_OP_CONST && ft.get_req() == true))
				return false;
			return true;
		}

		MsgFields getFields( const std::string& msgType) const
		{
			MsgTypeToField::const_iterator i = m_messageFields.find( msgType );
			MsgFields m = i->second;
			return m;
		}

		FastTagVector getFieldTag( u32 tid ) const
		{
			TID2OrderedFields::const_iterator i = m_SortedFieldValues.find( tid );
			FastTagVector o = i->second;
			return o;
		}
		bool resetDictionary(FASTApi& iFast, FASTApi& oFast, unsigned int tid = 0 ) const;
		
		FastTagVector& getFieldTag( u32 tid )
		{
			TID2OrderedFields::iterator i = m_SortedFieldValues.find( tid );
			return i->second;
		}

		NonBodyFields getHeader( ) const
		{
			return m_headerFields;
		}

		NonBodyFields getTrailer( ) const
		{
			return m_trailerFields;
		}

		MsgFields getRequired( const std::string& msgType ) const
		{
			MsgTypeToField::const_iterator i = m_requiredFields.find( msgType );
			MsgFields m = i->second;
			return m;
		}


		bool isMsgField( const std::string& msgType, int field ) const
		{
			MsgTypeToField::const_iterator i = m_messageFields.find( msgType );
			if ( i == m_messageFields.end() ) return false;
			return i->second.find( field ) != i->second.end();
		}

		void addHeaderField( int field, bool required )
		{
			m_headerFields[ field ] = required;
		}
		void setHeaderField( int field, bool required )
		{
			m_headerFields[ field ] = required;
		}

		bool isHeaderField( int field ) const
		{
			return m_headerFields.find( field ) != m_headerFields.end();
		}

		void addTrailerField( int field, bool required )
		{
			m_trailerFields[ field ] = required;
		}
		void setTrailerOrderField( int field, bool required )
		{
			m_trailerFields[ field ] = required;
		}

		bool isTrailerField( int field ) const
		{
			return m_trailerFields.find( field ) != m_trailerFields.end();
		}

		void addFieldType( int field, TYPE::Type type )
		{
			m_fieldTypes[ field ] = type;
		}
		void addFASTFieldType( int field, TYPE::Type type )
		{
			FieldTypes::const_iterator i = m_fieldTypes.find( field );
			if ( i != m_fieldTypes.end() )
				return;
			m_fieldTypes[ field ] = type;
		}

		bool getFieldType( int field, TYPE::Type& type ) const
		{
			FieldTypes::const_iterator i = m_fieldTypes.find( field );
			if ( i == m_fieldTypes.end() ) return false;
			type = i->second;
			return true;
		}

		bool getFieldType( int field, eFAST_TYPE& FastType ) const
		{
			FieldTypes::const_iterator i = m_fieldTypes.find( field );
			if ( i == m_fieldTypes.end() ) return false;
			TYPE::Type type = i->second;
			FastType = TypeToFastType(type);
			return true;
		}
		bool getFieldOpearator( std::string op, eFAST_OP& FastOperator ) const
		{
			FastOperator = eFAST_OP_NONE;

			if ( op == "none" ) FastOperator = eFAST_OP_NONE;
			if ( op == "default" ) FastOperator = eFAST_OP_DEFAULT;
			if ( op == "constant" ) FastOperator = eFAST_OP_CONST;
			if ( op == "copy" ) FastOperator = eFAST_OP_COPY;
			if ( op == "increment" ) FastOperator = eFAST_OP_INCR;
			if ( op == "delta" ) FastOperator = eFAST_OP_DELTA;
			if ( op == "tail" ) FastOperator = eFAST_OP_TAIL;
			return true;
		}


		void addRequiredField( const std::string& msgType, int field )
		{
			m_requiredFields[ msgType ].insert( field );
		}

		bool isRequiredField( const std::string& msgType, int field ) const
		{
			MsgTypeToField::const_iterator i = m_requiredFields.find( msgType );
			if ( i == m_requiredFields.end() ) return false;
			return i->second.find( field ) != i->second.end();
		}

		bool isRequired( const std::string& msgType ) const
		{
			MsgTypeToField::const_iterator i = m_requiredFields.find( msgType );
			if ( i == m_requiredFields.end() ) return false;
			return true;
		}

		void addFieldValue( int field, const std::string& value )
		{
			m_fieldValues[ field ].insert( value );
		}

		bool hasFieldValue( int field ) const
		{
			FieldToValue::const_iterator i = m_fieldValues.find( field );
			return i != m_fieldValues.end();
		}

		bool isFieldValue( int field, const std::string& value ) const
		{
			FieldToValue::const_iterator i = m_fieldValues.find( field );
			if ( i == m_fieldValues.end() )
				return false;
			if( !isMultipleValueStringField( field ) )
				return i->second.find( value ) != i->second.end();

			// MultipleValueString
			std::string::size_type startPos = 0;
			std::string::size_type endPos = 0;
			do
			{
				endPos = value.find_first_of(' ', startPos);
				std::string singleValue =
					value.substr( startPos, endPos - startPos );
				if( i->second.find( singleValue ) == i->second.end() )
					return false;
				startPos = endPos + 1;
			} while( endPos != std::string::npos );
			return true;
		}

		void addGroup( const std::string& msg, int field, int delim,
			const DataDictionary& dataDictionary )
		{
			DataDictionary * pDD = new DataDictionary;
			*pDD = dataDictionary;
			pDD->setVersion( getVersion() );
			m_groups[ std::make_pair( msg, field ) ] = std::make_pair( delim, pDD );
		}

		bool isGroup( const std::string& msg, int field ) const
		{
			return m_groups.find( std::make_pair( msg, field ) ) != m_groups.end();
		}

		bool getGroup( const std::string& msg, int field, int& delim,
			const DataDictionary*& pDataDictionary ) const
		{
			FieldToGroup::const_iterator i =
				m_groups.find( std::make_pair( msg, field ) );
			if ( i == m_groups.end() ) return false;
			std::pair < int, DataDictionary* > pair = i->second;
			delim = pair.first;
			pDataDictionary = pair.second;
			return true;
		}

		bool isDataField( int field ) const
		{
			FieldTypes::const_iterator i = m_fieldTypes.find( field );
			return i != m_fieldTypes.end() && i->second == TYPE::Data;
		}

		bool isMultipleValueStringField( int field ) const
		{
			FieldTypes::const_iterator i = m_fieldTypes.find( field );
			return i != m_fieldTypes.end() && i->second == TYPE::MultipleValueString;
		}

		void checkFieldsOutOfOrder( bool value )
		{ m_checkFieldsOutOfOrder = value; }
		void checkFieldsHaveValues( bool value )
		{ m_checkFieldsHaveValues = value; }
		void checkUserDefinedFields( bool value )
		{ m_checkUserDefinedFields = value; }

		/// Validate a message.
		void validate( const Message& message ) throw( std::exception );
		DataDictionary& operator=( const DataDictionary& rhs );

	private:
		/// Iterate through fields while applying checks.
		void iterate( const FieldMap& map, const MsgType& msgType );

		/// Check if message type is defined in spec.
		void checkMsgType( const MsgType& msgType ) const
		{
			if ( !isMsgType( msgType.getValue() ) )
				throw InvalidMessageType();
		}

		/// If we need to check for the tag in the dictionary
		bool shouldCheckTag( const FieldBase& field ) const
		{
			if( !m_checkUserDefinedFields && field.getField() >= FIELD::UserMin )
				return false;
			else
				return true;
		}

		/// Check if field tag number is defined in spec.
		void checkValidTagNumber( const FieldBase& field ) const
			throw( InvalidTagNumber )
		{
			if( m_fields.find( field.getField() ) == m_fields.end() )
				throw InvalidTagNumber( field.getField() );
		}

		void checkValidFormat( const FieldBase& field ) const
			throw( IncorrectDataFormat )
		{
			try
			{
				TYPE::Type type = TYPE::Unknown;
				getFieldType( field.getField(), type );
				switch ( type )
				{
				case TYPE::String:
					STRING_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Char:
					CHAR_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Price:
					PRICE_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Int:
				case TYPE::UInt:
				case TYPE::Long:
				case TYPE::ULong:
					INT_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Amt:
					AMT_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Qty:
					QTY_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Currency:
					CURRENCY_CONVERTOR::convert( field.getString() ); break;
				case TYPE::MultipleValueString:
					MULTIPLEVALUESTRING_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Exchange:
					EXCHANGE_CONVERTOR::convert( field.getString() ); break;
				case TYPE::UtcTimeStamp:
					UTCTIMESTAMP_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Boolean:
					BOOLEAN_CONVERTOR::convert( field.getString() ); break;
				case TYPE::LocalMktDate:
					LOCALMKTDATE_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Data:
					DATA_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Float:
					FLOAT_CONVERTOR::convert( field.getString() ); break;
				case TYPE::PriceOffset:
					PRICEOFFSET_CONVERTOR::convert( field.getString() ); break;
				case TYPE::MonthYear:
					MONTHYEAR_CONVERTOR::convert( field.getString() ); break;
				case TYPE::DayOfMonth:
					DAYOFMONTH_CONVERTOR::convert( field.getString() ); break;
				case TYPE::UtcDate:
					UTCDATE_CONVERTOR::convert( field.getString() ); break;
				case TYPE::UtcTimeOnly:
					UTCTIMEONLY_CONVERTOR::convert( field.getString() ); break;
				case TYPE::NumInGroup:
					NUMINGROUP_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Percentage:
					PERCENTAGE_CONVERTOR::convert( field.getString() ); break;
				case TYPE::SeqNum:
				case TYPE::LongSeqNum:
					SEQNUM_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Length:
					LENGTH_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Country:
					COUNTRY_CONVERTOR::convert( field.getString() ); break;
				case TYPE::Unknown: break;
				}
			}
			catch ( FieldConvertError& )
			{ throw IncorrectDataFormat( field.getField(), field.getString() ); }
		}

		void checkValue( const FieldBase& field ) const
			throw( IncorrectTagValue )
		{
			if ( !hasFieldValue( field.getField() ) ) return ;

			const std::string& value = field.getString();
			if ( !isFieldValue( field.getField(), value ) )
				throw IncorrectTagValue( field.getField() );
		}

		/// Check if a field has a value.
		void checkHasValue( const FieldBase& field ) const
			throw( NoTagValue )
		{
			if ( m_checkFieldsHaveValues && !field.getString().length() )
				throw NoTagValue( field.getField() );
		}

		/// Check if a field is in this message type.
		void checkIsInMessage
			( const FieldBase& field, const MsgType& msgType ) const
			throw( TagNotDefinedForMessage )
		{
			if ( !isMsgField( msgType, field.getField() ) )
				throw TagNotDefinedForMessage( field.getField() );
		}

		/// Check if group count matches number of groups in
		void checkGroupCount
			( const FieldBase& field, const FieldMap& fieldMap, const MsgType& msgType ) const
			throw( RepeatingGroupCountMismatch )
		{
			int fieldNum = field.getField();
			if( isGroup(msgType, fieldNum) )
			{
				if( fieldMap.groupCount(fieldNum)
					!= IntConvertor::convert(field.getString()) )
					throw RepeatingGroupCountMismatch(fieldNum);
			}
		}

		/// Check if a message has all required fields.
		void checkHasRequired
			( const FieldMap& header, const FieldMap& body, const FieldMap& trailer,
			const MsgType& msgType ) const
			throw( RequiredTagMissing )
		{
			NonBodyFields::const_iterator iNBF;
			for( iNBF = m_headerFields.begin(); iNBF != m_headerFields.end(); ++iNBF )
			{
				if( iNBF->second == true && !header.isSetField(iNBF->first) )
					throw RequiredTagMissing( iNBF->first );
			}

			for( iNBF = m_trailerFields.begin(); iNBF != m_trailerFields.end(); ++iNBF )
			{
				if( iNBF->second == true && !trailer.isSetField(iNBF->first) )
					throw RequiredTagMissing( iNBF->first );
			}

			MsgTypeToField::const_iterator iM
				= m_requiredFields.find( msgType.getString() );
			if ( iM == m_requiredFields.end() ) return ;

			const MsgFields& fields = iM->second;
			MsgFields::const_iterator iF;
			for( iF = fields.begin(); iF != fields.end(); ++iF )
			{
				if( !body.isSetField(*iF) )
					throw RequiredTagMissing( *iF );
			}

			FieldMap::g_iterator groups;
			for( groups = body.g_begin(); groups != body.g_end(); ++groups )
			{
				int delim;
				const DataDictionary* DD = 0;
				int field = groups->first;
				if( getGroup( msgType.getValue(), field, delim, DD ) )
				{
					std::vector<FieldMap*>::const_iterator group;
					for( group = groups->second.begin(); group != groups->second.end(); ++group )
						DD->checkHasRequired( **group, **group, **group, msgType );
				}
			}
		}

		/// Read XML file using MSXML.
		void readMSXMLDOM( const std::string& );
		void readMSXML( const std::string& );
		/// Read XML file using libXML.
		void readLibXml( const std::string& );

		int lookupXMLFieldNumber( DOMDocument*, DOMNode* ) const;
		int lookupXMLFieldNumber( DOMDocument*, const std::string& name ) const;
		int addXMLComponentFields( DOMDocument*, DOMNode*, const std::string& msgtype, DataDictionary&, bool );
		void addXMLGroup( DOMDocument*, DOMNode*, const std::string& msgtype, DataDictionary&, bool, int tid = 0 );
		TYPE::Type XMLTypeToType( const std::string& xmlType ) const;
		eFAST_TYPE TypeToFastType( const TYPE::Type& type ) const;
		TYPE::Type XMLFASTTypeToType( const std::string& type ) const;


		bool m_hasVersion;
		bool m_checkFieldsOutOfOrder;
		bool m_checkFieldsHaveValues;
		bool m_checkUserDefinedFields;
		BeginString m_beginString;
		MsgTypeToField m_messageFields;
		MsgTypeToField m_requiredFields;
		MsgTypes m_messages;
		Fields m_fields;
		OrderedFields m_orderedFields;
		mutable OrderedFieldsArray m_orderedFieldsArray;
		NonBodyFields m_headerFields;
		NonBodyFields m_trailerFields;
		FieldTypes m_fieldTypes;
		FieldToValue m_fieldValues;
		FieldToName m_fieldNames;
		NameToField m_names;
		ValueToName m_valueNames;
		FieldToGroup m_groups;
		TemplateId2Msg m_TemplateId2Message;
		Msg2TemplateId m_Message2TemplateId;
		TID2OrderedFields m_SortedFieldValues;
		CheckOrderedFields m_CheckOrderedFields;

	};
}

#endif //FIX_DATADICTIONARY_H
